from django.core.cache import cache
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler

from AKapi.settings import const
from . import models
import re


class UserSerializer(serializers.ModelSerializer):
    username = serializers.CharField()  # 因为数据库定义了username为unique，所以要重写该字段

    class Meta:
        model = models.User
        fields = ['username', 'password', 'id']
        extra_kwargs = {
            'id': {'read_only': True},
            'password': {'write_only': True},
        }

    # 签发token
    def validate(self, attrs: dict):
        user = self._get_user(attrs=attrs)
        # 签发token
        token = self._get_token(user)
        # 放到context中，我在视图函类中可以取出来
        self.context['token'] = token
        self.context['user'] = user
        return attrs

    def _get_user(self, attrs: dict):
        username = attrs.get('username')
        password = attrs.get('password')
        if re.match(r'^1[3-9][0-9]{9}$', username):
            user = models.User.objects.filter(telephone=username).first()
        elif re.match(r'^.+@.+$', username):
            user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if user:
            ret = user.check_password(raw_password=password)
            if ret:
                return user
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('用户不存在')

    def _get_token(self, user):
        """
        给user签发token
        :param user:登录的用户
        :return:token
        """
        payload = jwt_payload_handler(user)  # 通过user对象获得payload(荷载)
        token = jwt_encode_handler(payload)  # 通过payload获得token
        return token


class CodeUserSerializer(serializers.ModelSerializer):
    code = serializers.CharField(max_length=4)
    class Meta:
        model = models.User
        fields = ['telephone', 'code']

    # 签发token
    def validate(self, attrs: dict):
        # 用户存在签发token
        user = self._get_user(attrs=attrs)
        token = self._get_token(user=user)
        self.context['token'] = token
        self.context['user'] = user
        return attrs

    def _get_user(self, attrs: dict):
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        # 取出原来的code
        cache_code = cache.get(const.PHONE_CACHE_KEY % telephone)
        if code == cache_code:
            # 验证码正确
            if re.match(r'^1[3-9][0-9]{9}', telephone):
                user = models.User.objects.filter(telephone=telephone).first()
                if user:
                    # 发送后把使用过的验证码射设为''
                    cache.set(const.PHONE_CACHE_KEY % telephone, '')
                    return user
                else:
                    raise ValidationError('用户不存在')
            else:
                raise ValidationError('手机号不合法')
        else:
            raise ValidationError('验证码错误或已超时')

    def _get_token(self, user):
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token


class UserRegisterSerializer(serializers.ModelSerializer):
    code = serializers.CharField(max_length=4, min_length=4, write_only=True)

    class Meta:
        model = models.User
        fields = ['telephone', 'code', 'password', 'username']
        extra_kwargs = {
            'password': {'max_length': 18, 'min_length': 6},
            'username': {'read_only': True},
        }

    def validate(self, attrs: dict):
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        cache_code = cache.get(const.PHONE_CACHE_KEY % telephone)
        if cache_code == code:
            if re.match(r'^1[3-9][0-9]{9}', telephone):
                user = models.User.objects.filter(telephone=telephone)
                print(user)
                if user:
                    raise ValidationError('用户已存在')
                # 设置用户名
                attrs['username'] = telephone
                # 将code从attrs中去除，方便后面望数据库写入东西
                attrs.pop('code')
                return attrs
            else:
                raise ValidationError('手机号不合法')
        else:
            raise ValidationError('验证码错误')

    # 重写create方法,因为多了一个code字段，数据库中没有该字段,且默认的create方法不会给password字段加密
    # 这个create方法是重写CreateModelMixin的create方法
    def create(self, validated_data):
        user = models.User.objects.create_user(**validated_data)  # create_user密码会加密
        return user